using Abp.Data;
using Abp.Dependency;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.Extensions;
using Abp.MultiTenancy;
using Abp.Runtime.Security;
using AbpCompanyName.AbpProjectName.EntityFrameworkCore;
using AbpCompanyName.AbpProjectName.EntityFrameworkCore.Seed;
using AbpCompanyName.AbpProjectName.MultiTenancy;
using System;
using System.Collections.Generic;
using System.Data.Common;

namespace AbpCompanyName.AbpProjectName.Migrator;

public class MultiTenantMigrateExecuter : ITransientDependency
{
    private readonly Log _log;
    private readonly AbpZeroDbMigrator _migrator;
    private readonly IRepository<Tenant> _tenantRepository;
    private readonly IDbPerTenantConnectionStringResolver _connectionStringResolver;

    public MultiTenantMigrateExecuter(
        AbpZeroDbMigrator migrator,
        IRepository<Tenant> tenantRepository,
        Log log,
        IDbPerTenantConnectionStringResolver connectionStringResolver)
    {
        _log = log;

        _migrator = migrator;
        _tenantRepository = tenantRepository;
        _connectionStringResolver = connectionStringResolver;
    }

    public bool Run(bool skipConnVerification)
    {
        var hostConnStr = CensorConnectionString(_connectionStringResolver.GetNameOrConnectionString(new ConnectionStringResolveArgs(MultiTenancySides.Host)));
        if (hostConnStr.IsNullOrWhiteSpace())
        {
            _log.Write("Configuration file should contain a connection string named 'Default'");
            return false;
        }

        _log.Write("Host database: " + ConnectionStringHelper.GetConnectionString(hostConnStr));
        if (!skipConnVerification)
        {
            _log.Write("Continue to migration for this host database and all tenants..? (Y/N): ");
            var command = Console.ReadLine();
            if (!command.IsIn("Y", "y"))
            {
                _log.Write("Migration canceled.");
                return false;
            }
        }

        _log.Write("HOST database migration started...");

        try
        {
            _migrator.CreateOrMigrateForHost(SeedHelper.SeedHostDb);
        }
        catch (Exception ex)
        {
            _log.Write("An error occured during migration of host database:");
            _log.Write(ex.ToString());
            _log.Write("Canceled migrations.");
            return false;
        }

        _log.Write("HOST database migration completed.");
        _log.Write("--------------------------------------------------------");

        var migratedDatabases = new HashSet<string>();
        var tenants = _tenantRepository.GetAllList(t => t.ConnectionString != null && t.ConnectionString != "");
        for (var i = 0; i < tenants.Count; i++)
        {
            var tenant = tenants[i];
            _log.Write(string.Format("Tenant database migration started... ({0} / {1})", (i + 1), tenants.Count));
            _log.Write("Name              : " + tenant.Name);
            _log.Write("TenancyName       : " + tenant.TenancyName);
            _log.Write("Tenant Id         : " + tenant.Id);
            _log.Write("Connection string : " + SimpleStringCipher.Instance.Decrypt(tenant.ConnectionString));

            if (!migratedDatabases.Contains(tenant.ConnectionString))
            {
                try
                {
                    _migrator.CreateOrMigrateForTenant(tenant);
                }
                catch (Exception ex)
                {
                    _log.Write("An error occured during migration of tenant database:");
                    _log.Write(ex.ToString());
                    _log.Write("Skipped this tenant and will continue for others...");
                }

                migratedDatabases.Add(tenant.ConnectionString);
            }
            else
            {
                _log.Write("This database has already migrated before (you have more than one tenant in same database). Skipping it....");
            }

            _log.Write(string.Format("Tenant database migration completed. ({0} / {1})", (i + 1), tenants.Count));
            _log.Write("--------------------------------------------------------");
        }

        _log.Write("All databases have been migrated.");

        return true;
    }

    private static string CensorConnectionString(string connectionString)
    {
        var builder = new DbConnectionStringBuilder { ConnectionString = connectionString };
        var keysToMask = new[] { "password", "pwd", "user id", "uid" };

        foreach (var key in keysToMask)
        {
            if (builder.ContainsKey(key))
            {
                builder[key] = "*****";
            }
        }

        return builder.ToString();
    }
}
